#include <set>
#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <dlfcn.h>
#include <cxxabi.h>
#include "error_codes.h"
#include "built_porno.h"

register void *rbp_reg asm ("rbp");
#define OFFS(x) (long)(reinterpret_cast<long>(rbp_reg) - reinterpret_cast<long>((void*)&(x)))

struct meta
{
	unsigned int num_of_ptr;
	long *offsets;

	meta()
	{
		num_of_ptr = 0;
		offsets = 0;
	}
};

struct call
{
	const void *address;
	const char *function;
	const char *object;
};


std::map <std::string, meta> meta_table;

meta get_meta (std::string name);
void print_meta(const char *tname);
void collect_roots ();
int backtrace(std::vector<call> &trace);
void* built_frame_address(int i);
void add_function (meta mt, std::string name);

void add_function (meta mt, std::string name)
{
	meta_table[name] = mt;
}

void* built_frame_address(int i)
{
	return builtin_frame_address(i);
}

int backtrace(std::vector<call> &trace)
{
	int num_of_func = 0;

	int i = 0;

	while (builtin_frame_address(i))
	{


		Dl_info dlinfo;
		call f_info;

		f_info.address = builtin_return_address(i);

		if (dladdr(f_info.address, &dlinfo) != 0)
		{
			const char *dfname;
			dfname = dlinfo.dli_sname;
			int status;
			char *truename;

			truename = __cxxabiv1::__cxa_demangle(dfname,NULL,0,&status);

			if (status == 0 && truename)
				f_info.function = truename;
			else
				f_info.function = dlinfo.dli_sname;

			f_info.object = dlinfo.dli_fname;
		}
		else
		{
			printf ("Something wrong with stack!\n");
			exit(FUNCTION_NOT_AVAILABLE);
		}

		trace.push_back(f_info);
		num_of_func++;
		i++;
	}

	return num_of_func;
}

meta get_meta (std::string name)
{
	return meta_table[name];
}

void print_meta(const char *tname)
{
	std::string name = std::string(tname, tname + strlen(tname));
	meta f_meta = get_meta(name);

	if (f_meta.num_of_ptr)
	{
		for (int i = 0; i < (int)f_meta.num_of_ptr; i++)
			printf("offset[%i] = %li ", i, f_meta.offsets[i]);
	}
	else
		printf("No meta data defined for this function");

	printf ("\n");
}

void collect_roots ()
{
	std::vector <call> trace;
	int num_of_func = backtrace(trace);

	printf("functions in stack:\n");
	for (int i = 0; i < num_of_func; i++)
	{
		printf("%s\n", trace[i].function);
		printf("fp = %p, ", built_frame_address(i + 1));
		print_meta(trace[i].function);
		printf("\n");
	}
	printf ("\n\n\n");
}

